﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using Microsoft.WVB.Silverlight.Infrastructure;

namespace Microsoft.WVB.Framework.AdControls
{
    public abstract class AdLayoutBase : Control, IDisposable
    {
        protected AdVisual SupportedVisual { get; set; }
        //protected abstract String StylePath { get; }

        private AdBase Ad { get; set; }
        
        private EventHandler adEndedHandler = null;
        private EventHandler<AdTrackedEventArgs> adTrackingHandler = null;
        private EventHandler adStoppedHandler = null;

        /// <summary>
        /// Gets the AdManager instance.
        /// </summary>
        public AdManager ViewModel { get; set; }

        /// <summary>
        /// Automatically bound to the AdManager CurrentPlaying property, this Dependency property
        /// is notifying the Control when an Ad needs to start and being displayed.
        /// </summary>
        public TimeSpan AdPlaying
        {
            get { return (TimeSpan)GetValue(AdPlayingProperty); }
            set { SetValue(AdPlayingProperty, value); }
        }
        public static readonly DependencyProperty AdPlayingProperty = null;

        /// <summary>
        /// Automatically bound to the AdManager CurrentContext property, this Dependency property
        /// is notifying the Control when an Ad needs to be buffered.
        /// </summary>
        public AdContext Context
        {
            get { return (AdContext)GetValue(ContextProperty); }
            set { SetValue(ContextProperty, value); }
        }
        public static readonly DependencyProperty ContextProperty = null;

        /// <summary>
        /// Static contructor responsible for initializing Dependency Properties.
        /// </summary>
        static AdLayoutBase()
        {
            ContextProperty = DependencyProperty.Register("Context", typeof(AdContext), typeof(AdLayoutBase), new PropertyMetadata(new PropertyChangedCallback(OnContextChanged)));
            AdPlayingProperty = DependencyProperty.Register("AdPlaying", typeof(TimeSpan), typeof(AdLayoutBase), new PropertyMetadata(new PropertyChangedCallback(OnAdPlayingChanged)));
        }

        /// <summary>
        /// Initialize a new instance of an AdControlBase class.
        /// </summary>
        public AdLayoutBase()
        {
            if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                //GetCreate important when we share a model
                ViewModel = IoC.GetCreate<AdManager>();
                DataContext = ViewModel;

                Binding indexBinding = new Binding("CurrentPlaying");
                indexBinding.Source = ViewModel;
                this.SetBinding(AdLayoutBase.AdPlayingProperty, indexBinding);

                Binding ctxBinding = new Binding("CurrentContext");
                ctxBinding.Source = ViewModel;
                this.SetBinding(AdLayoutBase.ContextProperty, ctxBinding);
            }

            //this.SetStyle();
        }

        /// <summary>
        /// Builds the visual tree for the AdControlBase when a new template is applied.
        /// Also, this is where internal logic hooks with UI elements part of the TemplatePart contract.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }

        static void OnContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((AdLayoutBase)d).OnContextChanged((AdContext)e.NewValue);
        }

        static void OnAdPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((AdLayoutBase)d).showAd((TimeSpan)e.NewValue);
        }

        private void showAd(TimeSpan timecode)
        {
            // This can happen if the user uses the slider to move the timeline and we
            // reach this timecode prior reaching the buffering timecode.
            if (Ad != null)
            {
                Ad.Start();
                this.OnBeginShowAd();
            }
        }

        /// <summary>
        /// Called when the AdManager context is changing. This method is responsible for filling the Ad
        /// property with a BaseAd-derived class containing the Ad in the proper format defined in the AdContext.
        /// </summary>
        /// <param name="adContext">A newly AdContext set from the AdManager</param>
        protected void OnContextChanged(AdContext adContext)
        {
            AdBuilder ab = new AdBuilder();
            this.Ad = ab.Create(adContext.Format);
            this.OnBufferingAd((FrameworkElement)Ad);

            if (this.Ad != null)
            {

                if (adEndedHandler == null)
                {
                    adEndedHandler = delegate(object sender, EventArgs e)
                    {
                        this.OnEndShowAd();
                    };
                }
                Ad.AdEnded += adEndedHandler;

                if (adTrackingHandler == null)
                {
                    adTrackingHandler = delegate(object sender, AdTrackedEventArgs e)
                    {
                        this.OnTrackAdImpression(e);
                    };
                }
                Ad.AdImpressionTracked += adTrackingHandler;
                
                if (adStoppedHandler == null)
                {
                    adStoppedHandler = delegate(object sender, EventArgs e)
                    {
                        this.OnAdStopped();
                    };
                }
                Ad.AdStopped += adStoppedHandler;
            }

            Ad.Buffer(adContext);            
        }

        /// <summary>
        /// Called when a User has explicitly stopped the Ad delivery. This is especialy relevant for 
        /// Video Ads when use can cancel the Ad at any point in time.
        /// </summary>
        public abstract void OnAdStopped();
       
        /// <summary>
        /// Callback method called when Ad impression needs to be tracked back to a Back-End System.
        /// When Overriden by derived control, it provides developer the ability to specify custom
        /// actions.
        /// </summary>
        /// <param name="e">An <see cref="AdTrackedEventArgs"/> argument.</param>
        public virtual void OnTrackAdImpression(AdTrackedEventArgs e) 
        {
            Uri AdTracker = null;
            Uri ThrPartyAdTracker = null;

            switch (e.Timing)
            {
                case AdImpressionTiming.Beginning:
                    {
                        AdTracker = Context.ImpressionCallback;
                        ThrPartyAdTracker = Context.ThrdImpressionCallback;
                    }
                    break;
                case AdImpressionTiming.Middle:
                    {
                        AdTracker = Context.MidCallback;
                        ThrPartyAdTracker = Context.ThrdMidCallback;
                    }
                    break;
                case AdImpressionTiming.End:
                    {
                        AdTracker = Context.EndCallback;
                        ThrPartyAdTracker = Context.ThrdEndCallback;
                    }
                    break;
                default:
                    break;
            }

            if (AdTracker != null)
            {
                trackAds(AdTracker);
            }

            if (ThrPartyAdTracker != null)
            {
                trackAds(ThrPartyAdTracker);
            }
        }

        private void trackAds(Uri trackingUri)
        {
            WebClient wc = new WebClient();
            wc.OpenReadAsync(trackingUri);
        }

        /// <summary>
        /// When overriden in an AdControlBase-derived class, is providing a placeholder for 
        /// executing necessary actions in order to show the Ad in the UI.
        /// </summary>
        /// <param name="adIndex">A TimeSpan parameter that indicates the Timecode when the Ad will be displayed.
        /// <remarks>Only relevant for a Video Ad.</remarks></param>
        public abstract void OnBeginShowAd();

        /// <summary>
        /// When overriden in an AdControlBase-derived class, is providing a placeholder for 
        /// executing necessary actions in order to end the Ad from being displayed in the UI.
        /// </summary>
        public abstract void OnEndShowAd();

        /// <summary>
        /// When overriden in an AdControlBase-derived class, is providing a placeholder for 
        /// executing necessary actions in order to prepare the Ad for being displayed in the UI
        /// while it is buffering (MediaElement being buffering, Image.Source being set asynchronously ...)
        /// </summary>
        public abstract void OnBufferingAd(FrameworkElement Ad);

        #region IDisposable Members

        public virtual void Dispose()
        {
            if (Ad != null)
            {
                Ad.AdEnded -= adEndedHandler;
                Ad.AdImpressionTracked -= adTrackingHandler;
                Ad.AdStopped -= adStoppedHandler;
            }
        }

        #endregion
    }
}
